/*-----------------------------------------------------------------------------------*/
/* Nuvoton Technology Corporation confidential                                       */
/*                                                                                   */
/* Copyright (c) 2008 by Nuvoton Technology Corporation                              */
/* All rights reserved                                                               */
/*                                                                                   */
/*-----------------------------------------------------------------------------------*/
#ifdef ECOS
#include "drv_api.h"
#include "diag.h"
#include "wbtypes.h"
#include "wbio.h"
#define IRQ_DMAC  19
#define IRQ_FMI   20
#else
#include "wblib.h"
#endif

#include "nuc900_reg.h"
#include "fmi.h"
#include "nuc900_sic.h"
#include "nvtfat.h"

#ifdef ECOS
cyg_interrupt  dmac_interrupt, fmi_interrupt;
cyg_handle_t   dmac_interrupt_handle, fmi_interrupt_handle;
#endif


// global variable
UINT32 _fmi_uFMIReferenceClock;
BOOL volatile _fmi_bIsSDDataReady=FALSE, _fmi_bIsSMDataReady=FALSE;
BOOL volatile _fmi_bIsMSDataReady=FALSE, _fmi_bIsMSTimeOut=FALSE;
typedef void (*fmi_pvFunPtr)();   /* function pointer */
void (*fmiSD0RemoveFun)() = NULL;
void (*fmiSD0InsertFun)() = NULL;
void (*fmiSD1RemoveFun)() = NULL;
void (*fmiSD1InsertFun)() = NULL;
void (*fmiMS0RemoveFun)() = NULL;
void (*fmiMS0InsertFun)() = NULL;
void (*fmiMS1RemoveFun)() = NULL;
void (*fmiMS1InsertFun)() = NULL;

extern PDISK_T *pDisk_SD0, *pDisk_SD1, *pDisk_MS0, *pDisk_MS1;

#ifdef ECOS
static cyg_uint32 dmac_isr(cyg_vector_t vector, cyg_addrword_t data)
#else
void dmac_isr(void)
#endif
{
	// target abort !!!
	if (inpw(REG_DMACISR) & 0x01)
	{
		outpw(REG_DMACCSR, inpw(REG_DMACCSR)|0x03);
		outpw(REG_FMICSR, inpw(REG_FMICSR)|0x01);
		outpw(REG_DMACISR, 0x01);
	}
#ifdef ECOS
	cyg_drv_interrupt_acknowledge(IRQ_DMAC);
	return CYG_ISR_HANDLED;
#endif	
}

#ifdef ECOS
static cyg_uint32 fmiIntHandler(cyg_vector_t vector, cyg_addrword_t data)
#else
VOID fmiIntHandler()
#endif
{
	unsigned int volatile isr;

	// FMI data abort interrupt
	if (inpw(REG_FMIISR) == 0x01)
	{
		/* fmiResetAllEngine() */
		outpw(REG_FMICSR, inpw(REG_FMICSR) | 0x01);
		outpw(REG_FMIISR, 0x01);
	}

	// SD interrupt status
	isr = inpw(REG_SDISR);
	if (isr & 0x01)		// block down
	{
		_fmi_bIsSDDataReady = TRUE;
		outpw(REG_SDISR, 0x01);
	}

	if (isr & 0x100)	// port 0 card detect
	{
#ifndef _USE_DAT3_DETECT_
		if (inpw(REG_SDISR) & 0x10000)
		{
			pSD0->bIsCardInsert = FALSE;
			outpw(0xb8003018, inpw(0xb8003018) | 0x100);	// set gpiod-8 output high
			if (fmiSD0RemoveFun != NULL)
				(*fmiSD0RemoveFun)(pDisk_SD0);
		}
		else
		{
			pSD0->bIsCardInsert = TRUE;
			outpw(0xb8003018, inpw(0xb8003018) & 0xfffffeff);	// set gpiod-8 output low
			if (fmiSD0InsertFun != NULL)
				(*fmiSD0InsertFun)();
		}
#else
		if (inpw(REG_SDISR) & 0x10000)
		{
			outpw(REG_SDIER, inpw(REG_SDIER)&0xfffffeff);
			pSD0->bIsCardInsert = TRUE;
			if (fmiSD0InsertFun != NULL)
				(*fmiSD0InsertFun)();
		}
#endif
		outpw(REG_SDISR, 0x100);
	}

	if (isr & 0x200)	// port 1 card detect
	{
#ifndef _USE_DAT3_DETECT_
		if (inpw(REG_SDISR) & 0x20000)
		{
			pSD1->bIsCardInsert = FALSE;
			outpw(0xb8003048, inpw(0xb8003048) | 0x20);	// set gpiog-5 output high
			if (fmiSD1RemoveFun != NULL)
				(*fmiSD1RemoveFun)(pDisk_SD1);
		}
		else
		{
			pSD1->bIsCardInsert = TRUE;
			outpw(0xb8003048, inpw(0xb8003048) & 0xffffffdf);	// set gpiog-5 output low
			if (fmiSD1InsertFun != NULL)
				(*fmiSD1InsertFun)();
		}
#else
		if (inpw(REG_SDISR) & 0x20000)
		{
			pSD1->bIsCardInsert = TRUE;
			if (fmiSD1InsertFun != NULL)
				(*fmiSD1InsertFun)();
			outpw(REG_SDIER, inpw(REG_SDIER)&0xfffffdff);
		}
#endif
		outpw(REG_SDISR, 0x200);
	}

	// SM interrupt status
	isr = inpw(REG_SMISR);
	if (isr & 0x01)
	{
		_fmi_bIsSMDataReady = TRUE;
		outpw(REG_SMISR, 0x01);
	}

	// MS interrupt status
	isr = inpw(REG_MSISR);
	if (isr & 0x01)
	{
		_fmi_bIsMSDataReady = TRUE;
		outpw(REG_MSISR, 0x01);
	}

	if (isr & 0x04)
	{
		_fmi_bIsMSTimeOut = TRUE;
		outpw(REG_MSISR, 0x04);
	}

	if (isr & 0x10000)	// port 0 card detect
	{
		if (inpw(REG_MSISR) & 0x1000000)
		{
			pMS0->bIsCardInsert = FALSE;
			if (fmiMS0RemoveFun != NULL)
				(*fmiMS0RemoveFun)(pDisk_MS0);
		}
		else
		{
			pMS0->bIsCardInsert = TRUE;
			if (fmiMS0InsertFun != NULL)
				(*fmiMS0InsertFun)();
		}
		outpw(REG_MSISR, 0x10000);
	}

	if (isr & 0x20000)	// port 1 card detect
	{
		if (inpw(REG_MSISR) & 0x2000000)
		{
			pMS1->bIsCardInsert = FALSE;
			if (fmiMS1RemoveFun != NULL)
				(*fmiMS1RemoveFun)(pDisk_MS1);
		}
		else
		{
			pMS1->bIsCardInsert = TRUE;
			if (fmiMS1InsertFun != NULL)
				(*fmiMS1InsertFun)();
		}
		outpw(REG_MSISR, 0x20000);
	}
#ifdef ECOS
	cyg_drv_interrupt_acknowledge(IRQ_FMI);
	return CYG_ISR_HANDLED;
#endif	
}

VOID fmiSetCallBack(UINT32 uCard, PVOID pvRemove, PVOID pvInsert)
{
	switch (uCard)
	{
		case FMI_SD_CARD0:
			fmiSD0RemoveFun = (fmi_pvFunPtr)pvRemove;
			fmiSD0InsertFun = (fmi_pvFunPtr)pvInsert;
			break;

		case FMI_SD_CARD1:
			fmiSD1RemoveFun = (fmi_pvFunPtr)pvRemove;
			fmiSD1InsertFun = (fmi_pvFunPtr)pvInsert;
			break;

		case FMI_MS_CARD0:
			fmiMS0RemoveFun = (fmi_pvFunPtr)pvRemove;
			fmiMS0InsertFun = (fmi_pvFunPtr)pvInsert;
			break;

		case FMI_MS_CARD1:
			fmiMS1RemoveFun = (fmi_pvFunPtr)pvRemove;
			fmiMS1InsertFun = (fmi_pvFunPtr)pvInsert;
			break;
	}
}


VOID fmiInitDevice()
{
	// enable FMI
	outpw(REG_CLKEN, inpw(REG_CLKEN) | 0x10);

	/* Install ISR */
#ifdef ECOS
	cyg_drv_interrupt_create(IRQ_DMAC, 10, 0, dmac_isr, NULL,
								&dmac_interrupt_handle, &dmac_interrupt);
    cyg_drv_interrupt_attach(dmac_interrupt_handle);
	cyg_drv_interrupt_create(IRQ_FMI, 10, 0, fmiIntHandler, NULL,
								&fmi_interrupt_handle, &fmi_interrupt);
    cyg_drv_interrupt_attach(fmi_interrupt_handle);
#else
	sysInstallISR(IRQ_LEVEL_1, IRQ_DMAC, (PVOID)dmac_isr);
    sysInstallISR(IRQ_LEVEL_1, IRQ_FMI, (PVOID)fmiIntHandler);
#endif

#ifndef ECOS
    /* enable CPSR I bit */
    sysSetLocalInterrupt(ENABLE_IRQ);
#endif

	// DMAC Initial
	outpw(REG_DMACCSR, 0x00000003);
	outpw(REG_DMACCSR, 0x00000001);
	outpw(REG_DMACIER, 0x00000001);

#ifdef ECOS
    cyg_drv_interrupt_unmask(IRQ_DMAC);
    cyg_drv_interrupt_unmask(IRQ_FMI);
#else
	sysEnableInterrupt(IRQ_DMAC);
	sysEnableInterrupt(IRQ_FMI);
#endif

	outpw(REG_FMICSR, 0x01);		// reset FMI engine
}

VOID fmiSetFMIReferenceClock(UINT32 uClock)
{
	_fmi_uFMIReferenceClock = uClock;	// kHz
}

PDISK_T *fmiGetpDisk(UINT32 uCard)
{
	switch (uCard)
	{
		case FMI_SD_CARD0:
			return pDisk_SD0;

		case FMI_SD_CARD1:
			return pDisk_SD1;

		case FMI_MS_CARD0:
			return pDisk_MS0;

		case FMI_MS_CARD1:
			return pDisk_MS1;
	}
	return 0;
}



